//
//  LuaP.cpp
//  LuaP
//
//  Created by liyong on 2016/10/18.
//  Copyright © 2016年 liyong. All rights reserved.
//

#include "LuaP.hpp"
#include <chrono>
#include "easylogging++.h"

using namespace std;

INITIALIZE_EASYLOGGINGPP


extern "C" {
    static void callhook(lua_State *L, lua_Debug *ar) {
        lua_Debug previous_ar;
        lua_getstack(L, 1, &previous_ar);
        lua_getinfo(L, "n", ar);
        LuaP::getInstance().CallFunc(ar);
        
    }
}

void LuaP::SetLogPath(string path) {
    logPath = path;
    
    el::Configurations defaultConf;
    defaultConf.setToDefault();
    defaultConf.set(el::Level::Info, el::ConfigurationType::Enabled, "true");
    defaultConf.set(el::Level::Info, el::ConfigurationType::Format, "%datetime %msg");
    defaultConf.set(el::Level::Info, el::ConfigurationType::ToFile, "true");
    defaultConf.set(el::Level::Info, el::ConfigurationType::Filename, logPath);
    
    el::Loggers::reconfigureLogger("default", defaultConf);
}

void LuaP::Start(lua_State *L){
    
    lua_sethook(L, (lua_Hook)callhook, LUA_MASKCALL | LUA_MASKRET, 0);
}

void LuaP::CallFunc(lua_Debug *ar) {
    if(ar->name == NULL) {
        return;
    }
    
    auto ret = functionCalls.find(ar->name);
    if(ret == functionCalls.end()){
        if(ar->event == 1) {
            return;
        }
        
        shared_ptr<FunctionCallInfo> fc(new FunctionCallInfo());
        functionCalls[ar->name] = fc;
        fc->numbers = 0;
        fc->time = 0;
    }
    
    shared_ptr<FunctionCallInfo> fc1 = functionCalls[ar->name];
    
    if(!ar->event) {
        fc1->startTime = chrono::steady_clock::now();
    }else {
        auto diff = chrono::steady_clock::now()-fc1->startTime;
        auto d = chrono::duration<double, nano> (diff).count();
        fc1->time += d;
        fc1->numbers++;
    }
}

void LuaP::WriteLog() {
    for(auto f : functionCalls){
        auto fn = f.first;
        auto fc = f.second;
        LOG(INFO) << "FC:" << fn << ":" << fc->time << ":" << fc->numbers;
    }
    
    functionCalls.clear();
}

void LuaP::Stop() {
    el::Loggers::flushAll();
}

extern "C" {
    
    static int profiler_init(lua_State *L){
        LuaP::getInstance().Start(L);
        return 0;
    }
    static int profiler_stop(lua_State *L) {
        LuaP::getInstance().Stop();
        return 0;
    }
    
    static int profiler_setLogPath(lua_State *L) {
        const char*path = lua_tostring(L, 1);
        LuaP::getInstance().SetLogPath(path);
        return 0;
    }
    static int profiler_writeLog(lua_State *L) {
        LuaP::getInstance().WriteLog();
        return 0;
    }
    
    static const luaL_Reg prof_funcs[] = {
        {"start", profiler_init},
        {"stop", profiler_stop},
        {"setLogPath", profiler_setLogPath},
        {"writeLog", profiler_writeLog},
        {NULL, NULL},
    };
    int luaopen_luaProfiler(lua_State *L) {
        luaL_openlib(L, "luaProfiler", prof_funcs, 0);
        return 1;
    }
}
